/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			ResTexture.d
	Description:	texture resource
	Date:			23.11.2007 by Digited
**/
module 	moonglide.resources.resobjects.lowlevel.ResTextureData;

import moonglide.resources.Management;

public import
	derelict.devil.il,
	derelict.devil.ilu;

enum : ubyte
{
	Tex_1d = 1,
	Tex_2d = 0,
	Tex_3d = 2,
	Tex_Cube = 3
}

enum ETextureFilter
{
	tfBilinear,
	tfTrilinear,
	tfAnisotropy
}

enum ETexTarget: ubyte
{
	Tex_Cube_X_Pos = 0,
	Tex_Cube_X_Neg = 1,
	Tex_Cube_Y_Pos = 2,
	Tex_Cube_Y_Neg = 3,
	Tex_Cube_Z_Pos = 4,
	Tex_Cube_Z_Neg = 5
}


enum : uint
{
    IL_MOON_DEPTH24 = 0x81A6
}

//================================================================
/// data texture resource class
//================================================================
class CResTextureData : IResource
{
	protected:
		int _width, _height, _bpp, _format, _type, _numComponents = 0;
		bool _dataNull = false; /// if proc tex, then we store data in openGL, not in DevIL

		this( char[] idname, bool proc )
		{
			super( idname );
			ilGenImages( 1, &ilTextureID );
			_dataNull = proc;
		}

	public:
		uint	ilTextureID = 0,
				texType = 0,
				gtexType;
			/* texType may be in OGL :
				GL_TEXTURE_1D,
				GL_TEXTURE_2D,
				GL_TEXTURE_3D,

				GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
				GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
				GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
				GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
			*/

		bool	compressed,
				needToUpdate = false,
				useMipMap = true,
				clampToEdge = false;

		ETextureFilter filter = ETextureFilter.tfTrilinear;
		int filterLevel = 0; // anisotropy level;

		int width()			{ return _width; }
		int height()		{ return _height; }
		int bpp()			{ return _bpp; }
		int format()		{ return _format; }
		int type()			{ return _type; }
		int numComponents()	{ return _numComponents; }

		void setProcedural( bool pr ) { _dataNull = pr; }
		bool isProcedural() { return _dataNull; }

		void UpdateInfo()
		{
		    if( !_dataNull )
		    {
                uint a = ilGetInteger( IL_ACTIVE_IMAGE );
                ilBindImage( ilTextureID );
                _width = ilGetInteger( IL_IMAGE_WIDTH );
                _height = ilGetInteger( IL_IMAGE_HEIGHT );

            // 	IL_BGR, IL_BGRA, IL_COLOUR_INDEX, IL_LUMINANCE, IL_LUMINANCE_ALPHA, IL_RGB, IL_RGBA ,   IL_MOON_DEPTH24
                if( _format != IL_MOON_DEPTH24 )
                    _format = ilGetInteger( IL_IMAGE_FORMAT );

            // 	IL_BYTE, IL_DOUBLE, IL_FLOAT, IL_INT, IL_SHORT, IL_UNSIGNED_BYTE, IL_UNSIGNED_INT, IL_UNSIGNED_SHORT
                _type = ilGetInteger( IL_IMAGE_TYPE );
                _bpp = ilGetInteger( IL_IMAGE_BITS_PER_PIXEL );
		    }

            switch( _format )
            {
                case IL_MOON_DEPTH24, IL_COLOUR_INDEX, IL_LUMINANCE, IL_LUMINANCE_ALPHA: _numComponents = 1; break;
                case IL_BGR, IL_RGB: _numComponents = 3; 	break;
                case IL_BGRA, IL_RGBA: _numComponents = 4;	break;
                default: errorLog( "ResTextureData.UpdateInfo: unknown switch" );
            }
		}

		Color4ub readPixel(int x, int y)
		{
            if( _dataNull )
                return Color4ub(1,1,1,1);
		    ilBindImage( ilTextureID );
		    Color4ub col;
		    ilCopyPixels( x,y,0, 1,1,1, format, type, col.ptr );
		    return col;
		}

		void ConvertTo( CResTextureData ToTex )
		{
		    moonAssert( !_dataNull, "empty code",__FILE__,__LINE__ );
			ilBindImage( ilTextureID );
			ilConvertImage( ToTex.format, ToTex.type );
			UpdateInfo();
			needToUpdate = true;
		}

		void GetCopy( CResTextureData fromTex )
		{
			moonAssert( !_dataNull, "empty code",__FILE__,__LINE__ );
			ilDeleteImages( 1, &ilTextureID );
			ilBindImage( fromTex.ilTextureID );
			ilTextureID = ilCloneCurImage();
			UpdateInfo();
			needToUpdate = true;
		}

		SRegion GetAllRegion()
		{
			return SRegion( width, height, Point2i(0,0) );
		}

		void Resize( int wid, int hei )
		{
			if( _dataNull )
			{
			    _width = wid;
			    _height = hei;
			}
			else
			{
				int w = width;
				int h = height;

				ilBindImage( ilTextureID );

				uint clon = ilCloneCurImage();

				iluScale( wid, hei, 1 );
				ilClearImage();
				ilBlit(	clon, 0, 0, 0, 0, 0, 0, w, h, 1);
				ilDeleteImages( 1, &clon );

				UpdateInfo();
			}
			needToUpdate = true;
		}

/* /// not work correctly
		void CopyToTexRegion(CResTextureData fromD, Point2i toR)
		{
			//ilBindImage( fromD.ilTextureID );

			uint clon = fromD.ilTextureID;//ilCloneCurImage();
			//ilBindImage( clon );
			//ilConvertImage( format, type );

			ilBindImage( ilTextureID );

			ilBlit(	clon, toR.x, toR.y, 0, 0, 0, 0, fromD.width, fromD.height, 1);

			//ilDeleteImages( 1, &clon );
			needToUpdate = true;
		}
		*/

		void CopyToTexRegion( CResTextureData fromD, SRegion fromR, Point2i toR )
		{
		    moonAssert( !_dataNull, "empty code",__FILE__,__LINE__ );
			ilBindImage( fromD.ilTextureID );
			ubyte[] data;
			data.length = fromR.sizeX * fromR.sizeX * 4;
			ilCopyPixels( fromR.beginReg.x, fromR.beginReg.y, 0, fromR.sizeX, fromR.sizeY, 1, IL_BGRA, IL_UNSIGNED_BYTE, data.ptr );

			ilBindImage( ilTextureID );

			ilSetPixels( toR.x, toR.y, 0, fromR.sizeX, fromR.sizeY, 1, IL_BGRA, IL_UNSIGNED_BYTE, data.ptr );
		//	iluFlipImage();

			data.length = 0;

			needToUpdate = true;
		}

		void* GetData()
		{
	       if( _dataNull )
                return null;

			ilBindImage( ilTextureID );
			void* res = ilGetData();
			return res;
		}

		void DirectCopy( CResTextureData fromD )
		{
		    moonAssert( !_dataNull, "empty code",__FILE__,__LINE__ );

			if(( width == fromD.width ) && ( height == fromD.height ) &&
				( type == fromD.type ) && ( format == fromD.format ))
			{
				ilBindImage( fromD.ilTextureID );
				void* poi = ilGetData();
				ilBindImage( ilTextureID );
				ilSetData( poi );
				needToUpdate = true;
			}
		}

		void FillColor()
		{
		    moonAssert( !_dataNull, "empty code",__FILE__,__LINE__ );
			ilBindImage( ilTextureID );
			ilClearImage();
			needToUpdate = true;
		}

		void CreateTempTex( int w, int h, int Xformat, int Xtype )
		{
		    if( _dataNull )
		    {
		        _width	= w;
		        _height	= h;
		        _format	= Xformat;
		        _type	= Xtype;
		    }
		    else
		    {
                ilBindImage( ilTextureID );

                ilDefaultImage();
                ilConvertImage( Xformat, Xtype );
                iluScale( w, h, 1 );
		    }

		    UpdateInfo();
			needToUpdate = true;
		}

		void CreateEmptyTex( int w, int h, int Xformat, int Xtype )
		{
            ilBindImage( ilTextureID );

            ilConvertImage( Xformat, Xtype );
            iluScale( w, h, 1 );
            ilClearImage();

            UpdateInfo();

			needToUpdate = true;
		}

		//============================================================
		/// opCall - main resource getter
		//============================================================
		static CResTextureData opCall( char[] id, bool load = true, char[] errorResource = "" )
		{
			CResTextureData res;

            // check for registered ONLY in case we want to load resource
            if( load )
            {
                if( CResManager().Exists( id ))
                    debug
                    {
                        auto tmp = cast(CResTextureData)CResManager().GetResource( id );
                        moonAssert( tmp !is null, "casting GetResource to CResTextureData failed", __FILE__, __LINE__ );
                        return tmp;
                    }
                    else
                    {
                        return cast(CResTextureData)CResManager().GetResource( id );
                    }

                res = new CResTextureData( id, false );

                _Load( res, errorResource );
                debug moonAssert( res !is null, "loading CResTextureData returned null", __FILE__, __LINE__ );
            }
            else
            {
            	// if we do not load, just gen unique name and create new with it.
				CResManager().GenerateUniqueName( id );
				res = new CResTextureData( id, true );
				_New( res );
            }

			return res;
		}

		// destructor
		~this()
		{
			ilDeleteImages( 1, &ilTextureID );
		}
}